package gui.event;

import gui.GameConstants;
import gui.drawers.Drawer;

import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;

import dal.MapStruct;
import dal.object.*;

/**
 * @author nosxp
 *	Contains and calculates the selected indexes when mouse click is performed on the map.
 * It's used in the map editor and for detecting collisions.
 */
public class HexGrid {
	private static final int R;
    private static final int W;
    private static final int W50;
    private static final int R50;
    private static final int S;
    private static final float SQRT3;
    private final int N;
    private final int M;
    static {
    	R = GameConstants.picHeight50;
        W = GameConstants.picWidth100;
        W50 = GameConstants.picWidth50;
        R50 = GameConstants.picHeight25;
        S = (R * 3) >> 1;					// S = R + (R >> 1);
        SQRT3 = (float) Math.sqrt(3D);
    }
    private int i, j;
    private MapStruct ms;
   
    /**
     * @param ms
     * 	contains the structure the map.
     */
	public HexGrid(MapStruct ms) {
		this.N = (int) ms.getDimension().getWidth();
		this.M = (int) ms.getDimension().getHeight();
		this.ms = ms;
	}
	
	/**	Matrix indexes
	 * @param i
	 * 	the selected row index
	 * @param j
	 * 	the selected column index
	 */
    public void setSelectedIndex(int i, int j){
    	this.i = i;
    	this.j = j;
    }
    
    /**
     * @return
     * 	the latest selected horizontal j index
     */
    public int getHorizontalIndex(){
    	return this.j;
    }
    
    /**
     * @return
     * 	the latest selected vertical i index
     */
    public int getVerticalIndex(){
    	return this.i;
    }
    
    /** Vector index
     * @return
     * 	the index of the selected element or
     * 	-1 if the selected index is out of range.
     */
    public int getIndex(){
    	if(i < 0 || j < 0 || i >= M || j >= N) {
    		return -1;
    	} else {
    		return i * N + j;
    	}
    }

    /**
     * @param x and @param y are coordinates on the map
     * 	Converts the given coordinates to the corresponding indexes.
     * @return
     * 	vector index of the element on the specified point
     */
    public int convertToIndex(int x, int y){
		i = (int) Math.floor((float) y / S);
        x = x - (( (i & 1) * W) >> 1);
        j = (int) Math.floor((float) x / W);
        x = x - j * W;

        if ( (y - i * S) <= Math.abs(R50 - x / SQRT3) ) {					/*Math.abs(R50 - R * (float) xt / W)) {*/
        	j = j + (i & 1) - ((x < W50) ? 1 : 0);
        	--i;
        }
        
        return (i < 0 || j < 0 || i >= M || j >= N) ? -1 : (i * N + j);
    }
	
    /** used by the convertToIndex()
     * @param xt
     * 	x coordinate of the picked point
     * @return
     * 	1 if the selected j index have to be on the on the left side, (default = right side)
     *  or 0 if it's in the right place  
     */
//	private int delta(int xt){
//    	return (xt < W50) ? 1 : 0;
//    }
    
	/**
	 * @param n
	 * 	an integer
	 * @return
	 * 	0 if n is even or
	 * 	1 if n is odd.
	 */
//    private int bin(int n){
//    	return n & 1;
//    }
    
    /**
     * @param mo
     * 	a MapObject
     * @return
     * 	a new object according to the type of the initial object or
     * 	null if the parameter not a MapObject
     */
	public static MapObject newInstace(Object mo){
		try {
			return (MapObject) mo.getClass().newInstance();
		} catch (Exception e){
			JOptionPane.showMessageDialog(null, "Invalid Object\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
		} 
		return null;
	}
	
	/**
	 * @param mo
	 * 	a MapObject
	 * @return
	 * 	a Drawer containing the same type of objects or
	 * 	null if no such Drawer
	 */
	public static Drawer getDrawerByType(MapObject mo, JLayeredPane jLayers) {
		if(mo instanceof Ground) {
    		return (Drawer) jLayers.getComponentsInLayer(GameConstants.GROUND_LAYER)[0];
    	} else if(mo instanceof Rock) {
    		return (Drawer) jLayers.getComponentsInLayer(GameConstants.ROCK_LAYER)[0];
    	} else if(mo instanceof Ball) {
    		return (Drawer) jLayers.getComponentsInLayer(GameConstants.BALL_LAYER)[0];
    	} else if(mo instanceof Tool) {
    		return (Drawer) jLayers.getComponentsInLayer(GameConstants.TOOL_LAYER)[0];
    	} else if(mo instanceof Attacker) {
    		return null;
    	} else {
    		return null;
    	}
	}

	/**
	 * @return
	 * 	returns the structure of the map
	 */
	public MapStruct getMapStruct() {
		return this.ms;
	}
}
